//********************************************************************
//	created:	15:11:2012   23:33
//	filename: 	addresshelper.cpp
//	author:		tiamo	
//	purpose:	address helper
//********************************************************************

#include "stdafx.h"
#include "addresshelper.h"

namespace network
{
	//
	// to string
	//
	UniString AddressHelper::toString(bool includePort) const
	{
		wchar_t tempBuffer[128]												= {0};
		char addressBuffer[128]												= {0};
		if(mAddress.v4.sin_family == AF_INET)
			inet_ntop(AF_INET, const_cast<in_addr*>(&mAddress.v4.sin_addr), addressBuffer, ARRAYSIZE(addressBuffer));
		else if(mAddress.v6.sin6_family == AF_INET6)
			inet_ntop(AF_INET6, const_cast<in6_addr*>(&mAddress.v6.sin6_addr), addressBuffer, ARRAYSIZE(addressBuffer));

		UniString ip														= utils::utf8ToWide(addressBuffer);
		if(!includePort)
			return ip;
		else if(mAddress.v4.sin_family == AF_INET)
			platform::snwprintf(tempBuffer, ARRAYSIZE(tempBuffer), L"%ls:%u", ip.c_str(), ntohs(mAddress.v4.sin_port));
		else
			platform::snwprintf(tempBuffer, ARRAYSIZE(tempBuffer), L"[%ls]:%u", ip.c_str(), ntohs(mAddress.v6.sin6_port));

		return tempBuffer;
	}

	//
	// from string
	//
	bool AddressHelper::fromString(UniString const& ip, uint16_t port)
	{
		Utf8String ip8														= utils::wideToUtf8(ip);
		bool converted														= false;
		if(inet_pton(AF_INET, ip8.c_str(), &mAddress.v4.sin_addr) > 0)
		{
			mAddress.v4.sin_family											= AF_INET;
			mAddress.v4.sin_port											= htons(port);
			converted														= true;
		}

		if(inet_pton(AF_INET6, ip8.c_str(), &mAddress.v6.sin6_addr) > 0)
		{
			mAddress.v6.sin6_family											= AF_INET6;
			mAddress.v6.sin6_port											= htons(port);
			converted														= true;
		}
		return converted;
	}

	//
	// get address list
	//
	std::list<AddressHelper> AddressHelper::getAddressList(UniString const& hostName, uint16_t port)
	{
		//
		// query address info
		//
		std::list<AddressHelper> retValue;
		struct addrinfo hints												= {0};
		hints.ai_flags														= AI_ALL;
		hints.ai_socktype													= SOCK_STREAM;
		hints.ai_protocol													= IPPROTO_TCP;
		struct addrinfo* result												= nullptr;
		struct addrinfo* i													= nullptr;
		ON_BLOCK_EXIT([&result](){if(result) freeaddrinfo(result);});
		if(!getaddrinfo(utils::wideToUtf8(hostName).c_str(), nullptr, &hints, &result))
		{
			for(i = result; i; i = i->ai_next)
			{
				AddressHelper address;
				if(address.attach(i->ai_addr, static_cast<uint32_t>(i->ai_addrlen), port))
					retValue.push_back(address);
			}
		}
		else
		{
			LogError(L"AddressHelper: unable to get host by name(%ls)", hostName.c_str());
		}
		return retValue;
	}

	//
	// attach
	//
	bool AddressHelper::attach(sockaddr const* address, uint32_t length, uint16_t port)
	{
		if(length > sizeof(mAddress)) 
			return false;

		if(address->sa_family == AF_INET)
		{
			memcpy(&mAddress.v4, address, length);
			if(port)
				mAddress.v4.sin_port										= htons(port);

			return true;
		}
		else if(address->sa_family == AF_INET6)
		{
			memcpy(&mAddress.v6, address, length);
			if(port)
				mAddress.v6.sin6_port										= htons(port);

			return true;
		}
		return false;
	}
}
